package utils;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.IdUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.deepoove.poi.XWPFTemplate;
import com.deepoove.poi.config.Configure;
import com.deepoove.poi.data.*;
import com.spire.doc.Document;
import com.spire.doc.FileFormat;
import entity.AddrModel;
import entity.Book;
import org.apache.commons.io.FileUtils;

import policy.LoopRowTableHyperlinkRenderPolicy;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.List;

/**
 * @author yousj
 * @since 2022/4/30
 */
public class PoiUtil {

    // private static final String OUT_PATH = "/tmp/";

    private static final String OUT_PATH = "target\\";


    public static void main(String[] args) throws Exception {
        PoiUtil poiUtil = new PoiUtil();
        File targetFile = poiUtil.createWord();
    }


    public File createWord() throws Exception {

        Date date = new Date();


        Map<String, Object> data = new HashMap<>();
        // EL表达式处理
        data.put("time", new Date());

        // 空处理, 模板文件中使用表达式 eg:
        // {{?isEmpty != null}} 展示数据 {{/}}
        data.put("isEmpty", null);

        data.put("name", "小明同学");
        // 超链接
        data.put("link", new HyperlinkTextRenderData("POI-TL中文文档\n", "http://deepoove.com/poi-tl"));

        // 图片
        data.put("logo", pictureBuilder("1.jpg")
                .size(300, 300)
                .create());

        data.put("total", 100000000);
        data.put("one", "Java");
        data.put("two", "C");
        data.put("three", "Python");
        data.put("four", "JavaScript");

        // 图表
        String[] names = {"Java", "C", "Go", "JavaScript", "Python", "Shell"};
        Integer[] counts = new Integer[]{9999, 8888, 7777, 6666, 5555, 3333};

        // 饼图
        ChartSingleSeriesRenderData pie = Charts
                .ofSingleSeries("图书数量统计", names)
                .series("category", counts)
                .create();
        data.put("pieChart", pie);

        // 柱状图
        ChartMultiSeriesRenderData chart = Charts
                .ofMultiSeries("图书数量统计", names)
                .addSeries("category", counts)
                .create();
        data.put("barChart", chart);

        // 嵌套
        List<AddrModel> subData = new ArrayList<>();
        subData.add(new AddrModel("浙江省", "杭州市", "西湖区"));
        subData.add(new AddrModel("河南省", "郑州市", "二七区"));
        subData.add(new AddrModel("河南省", "信阳市", "光山县"));

        InputStream subFileStream = this.getClass().getClassLoader().getResourceAsStream("template/sub.docx");
        data.put("nested", Includes.ofStream(subFileStream).setRenderModel(subData).create());

        // Foreach Loop循环
        data.put("sections", Arrays.asList(
                new HashMap<String, String>() {{
                    put("name", "张三\n");
                }},
                new HashMap<String, String>() {{
                    put("name", "李四\n");
                }},
                new HashMap<String, String>() {{
                    put("name", "王五\n");
                }},
                new HashMap<String, String>() {{
                    put("name", "赵六");
                }}
        ));


        List<Book> books = new ArrayList<>();
        books.add(new Book("0001", "Java",
                "https://img10.360buyimg.com/n1/s200x200_jfs/t1/178430/19/15615/105679/60fa28baE585bf1ac/18d31810f2f4caee.jpg",
                "Java编程思想", 100, date));
        books.add(new Book("0002", "C",
                "https://img14.360buyimg.com/n1/s200x200_jfs/t1/179041/29/13172/93763/60e57f94Ecbb5cfc3/05c1b5002cd24471.jpg",
                "C语言程序设计", 99, date));
        books.add(new Book("0003", "Go",
                "https://img11.360buyimg.com/n1/s200x200_jfs/t1/134982/5/14334/232065/5f9e134eEcbe54be1/771d4003e7f39f3d.png",
                "Go语言核心编程", 98, date));
        books.add(new Book("0004", "JavaScript",
                "https://img11.360buyimg.com/n1/s200x200_jfs/t1/168129/39/12442/160552/604f0770Ea958d39e/eb8ded5b1973f8dc.jpg",
                "JavaScript权威指南", 97, date));
        books.add(new Book("0005", "Python",
                "https://img13.360buyimg.com/n1/s200x200_jfs/t1/30016/5/15264/149913/5cc149a8E38798467/1ccfd3a46c4492ec.jpg",
                "零基础入门学Python", 96, date));
        books.add(new Book("0006", "Shell", null, "Shell", 95, date));


        // 自定义表格处理超链接
        LoopRowTableHyperlinkRenderPolicy policy = new LoopRowTableHyperlinkRenderPolicy(1, "name");
        // 表格
        // 这里使用json主要是方便日期的格式化
        data.put("books", JSONArray.parseArray(JSON.toJSONString(books)));

        Configure config = Configure.builder()
                // isStrict = false , 关闭严格模式, 严格模式下key不存在会报错
                // 关闭后key不存在会被忽略
                .useSpringEL(false)
                // 表格绑定policy
                .bind("books", policy)
                .build();
        XWPFTemplate template = XWPFTemplate.
                compile(getFile("", "template/template.docx"), config).render(data);
        String path = newPath();
        template.writeToFile(path);

        // TOCRenderPolicy
        // Beta实验功能：目录，打开文档时会提示更新域
        // 参考 https://github.com/Sayi/poi-tl/issues/131
        // 不太好使

        // docx4j更新目录(特别慢, 有DMT ID耗尽的问题)
        // File targetFile = updateTocByDocx4j(path);
        File targetFile = updateTocBySpire(path);

        FileUtil.file(path).delete();

        return targetFile;
    }

    private File getFile(String path, String name) throws IOException {
        InputStream input = this.getClass().getClassLoader().getResourceAsStream(path + name);
        File file = new File(name);
        FileUtils.copyInputStreamToFile(input, file);
        return file;
    }

    private Pictures.PictureBuilder pictureBuilder(String name) {
        return Pictures.ofStream(this.getClass().getClassLoader().getResourceAsStream("static/images/" + name));
    }

    /**
     * spire.doc更新目录
     *
     * @param source 原始word文件
     * @return 更新目录后的word文件
     */
    private File updateTocBySpire(String source) {
        Document doc = new Document(source);
        String newPath = newPath();
        try {
            // 更新目录
            doc.updateTableOfContents();
            //保存文档
            doc.saveToFile(newPath, FileFormat.Auto);
        } finally {
            // 关闭文档
            doc.close();
        }
        return FileUtil.file(newPath);
    }

    private String newPath() {
        return OUT_PATH + IdUtil.fastSimpleUUID() + ".docx";
    }

}
